﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Bling.Core;
using Bling.WPF;
using Bling.Util;
using Bling.Shaders;
using Bling.Graphics;
using Bling.WPF3D;

namespace Bling.Tutorial {
  public class LightingExample : Canvas {
    public LightingExample() {
      CanvasBl canvas = this;
      // The image we wil l use in the example.
      var UseImage = new ImageBl(canvas) { 
        Height = 500,
        Source = new BitmapImage("Resources/Autumn.jpg".MakePackUri(typeof(Shaders))),
        CenterPosition = canvas.CenterSize 
      };
      // do some simple layout.
      PointBl At = new PointBl(0, 0);

      // build a couple of thumbs for the next shaders.
      ThumbBl thumbA = new ThumbBl(canvas) {
        CenterPosition = canvas.CenterSize, ZIndex = 1, CanDrag = true, Background = Brushes.Red,
      };
      ThumbBl thumbB = new ThumbBl(canvas) {
        CenterPosition = UseImage.LeftCenter, ZIndex = 1, CanDrag = true, Background = Brushes.Purple,
      };
      // A slider to control light.
      LabelSliderBl slider = new LabelSliderBl(canvas) {
        Minimum = 0, Maximum = 1, Value = 0.5,
        Width = UseImage.Width, LeftBottom = UseImage.LeftTop, Visibility = false,
      };

    Tex2D Normals0 = new ImageBrushBl(new BitmapImage("Resources/3977-normal0.jpg".MakePackUri(typeof(Shaders))));
    Tex2D Normals1 = new ImageBrushBl(new BitmapImage("Resources/glassNormal.jpg".MakePackUri(typeof(Shaders))));
    Tex2D Heights0 = new ImageBrushBl(new BitmapImage("Resources/3977-height0.jpg".MakePackUri(typeof(Shaders))));

    NormalMap Normals = (NormalMap)Normals0;
    HeightField Heights = (HeightField)Heights0;

    var light = new PointLightBl() { Position = new Point3DBl(thumbA.CenterPosition - UseImage.LeftTop, 170), Color = Colors.White };
    var light1 = new SpotLightCl() {
      Position = new Point3DBl(thumbB.CenterPosition - UseImage.LeftTop, 170),
      Direction = new Point3DBl(1, 0, -0.8).Normalize, Color = Colors.Yellow,

      InnerConeAngle = 5.ToDegrees(),
      OuterConeAngle = 15.ToDegrees(),

    };
    var eyeThumb = new ThumbBl(canvas) { CenterPosition = canvas.CenterSize + new PointBl(0, 30), CanDrag = true, Background = Colors.Blue };
    var eyePosition = new Point3DBl(eyeThumb.CenterPosition - UseImage.LeftTop, 100);
    var diffMaterial = new DiffuseMaterialCl() { Knob = Colors.White.Bl().ScRGB };
    var specMaterial = new SpecularMaterialCl() { Power = 1, Factor = 0.3, Knob = Colors.Red.Bl().ScRGB, Is2D = true };

    At = new ButtonBl(canvas) {
      LeftTop = At, Content = "Add only diffuse lighting, all normals are (0,0,1)",//so the normals come from Normal Map 
      Click = () => {
        slider.Visibility = false;
        UseImage.Source = new BitmapImage("Resources/Autumn.jpg".MakePackUri(typeof(Shaders)));
        UseImage.Effect.Custom = (input, uv) => diffMaterial.ApplyLights(light + light1)[new Point3DBl(uv * UseImage.Size, 0), new Point3DBl(0, 0, 1), eyePosition].ForPixel(input, uv);
      }
    }.RightTop;

    At = new ButtonBl(canvas) {
      LeftTop = At, Content = "Use Only NormalMap",//so the normals come from Normal Map 
      Click = () => {
        slider.Visibility = false;
        UseImage.Source = new BitmapImage("Resources/Autumn.jpg".MakePackUri(typeof(Shaders)));
        UseImage.Effect.Custom = (specMaterial + diffMaterial).ApplyLights(light + light1).Apply(Normals, UseImage.Size, eyePosition);
      }
    }.RightTop;

    At = new ButtonBl(canvas) {
      LeftTop = At, Content = "Use Only HeightMap",//so the normals come from Height Map, ang the Z value in the position is relative to Height map,with a scale 8*slider.Value,
                                                   //just for adjust the result to make normal is obvious
      Click = () => {
        slider.Visibility = true;
        slider.LabelName = "HeightScale";
        slider.Value = 1;
        UseImage.Source = new BitmapImage("Resources/Autumn.jpg".MakePackUri(typeof(Shaders)));
        UseImage.Effect.Custom = (specMaterial + diffMaterial).ApplyLights(light + light1).Apply(Heights, UseImage.Size.InsertZ(0.02 * slider.Value), 8 * slider.Value, eyePosition);
      }
    }.RightTop;

    At = new ButtonBl(canvas) {
      LeftTop = At, Content = "Use both NormalMap and heightMap",//so the normals come from Normal Map, but we use height map with the method Parallex Mapping to add some 
                                                                 //slight offset when loading normals or diffuse textures to make mapping more realistic
      Click = () => {
        slider.Visibility = true;
        slider.LabelName = "HeightScale'";//this HeightScale is a little different from the one in last example,this one often is to fit for the true proportion of the texture
                                          //and its real depth and so its value is usually small, like 0.02 * slider.Value in this example
        slider.Value = 1;

        UseImage.Source = new BitmapImage("Resources/3131-diffuse.jpg".MakePackUri(typeof(Shaders)));

        Tex2D Bump = new ImageBrushBl(new BitmapImage("Resources/3131-bump.jpg".MakePackUri(typeof(Shaders))));
        HeightField HeightMap = (HeightField)Bump;
        Tex2D Norm = new ImageBrushBl(new BitmapImage("Resources/3131-normal.jpg".MakePackUri(typeof(Shaders))));
        NormalMap NormalMap = (NormalMap)Norm;

        //Parallex Mapping depends on the eye position, so move the thumb to change the eye position, the "3d" result changes also
        UseImage.Effect.Custom = diffMaterial.ApplyLights(light + light1).Apply(HeightMap, NormalMap, UseImage.Size.InsertZ(0.02 * slider.Value), eyePosition);
      }
    }.RightTop;

    At = new ButtonBl(canvas) {
      LeftTop = At, Content = "Glass effect",
      Click = () => {
        slider.Visibility = true;
        slider.LabelName = "Thickness/4";
        slider.Value = 1;
        UseImage.Source = new BitmapImage("Resources/Autumn.jpg".MakePackUri(typeof(Shaders)));
        var GlassNormals = (NormalMap)Normals1;

        var material = new GlassMaterialCl() {
          Power = 3,
          Factor = 0.8,
          GlassColor = Colors.PaleTurquoise,
          RefractiveIndex = 1.5,
          Thickness = slider.Value * 4,
          Transparency = 0.75
        };
        UseImage.Effect.Custom = material.ApplyLights(light + light1).Apply(GlassNormals, UseImage.Size, eyePosition);

      }
    }.RightTop;

    At = new ButtonBl(canvas) {
      LeftTop = At, Content = "Simple diffraction",
      Click = () => {
        slider.Visibility = true;
        slider.Value = 1;
        slider.LabelName = "Distance/4";
        UseImage.Source = new BitmapImage("Resources/Autumn.jpg".MakePackUri(typeof(Shaders)));
        var center = UseImage.Size / 2d;
        var material = new DiffractionMaterialCl() {
          //Eye = eyePosition,
          TangentF = (UV, Position, Normal) => new Point3DBl((UV - 0.7) * UseImage.Size, 0).Cross(Normal).Normalize,
          Distance = 0.1 + 4 * slider.Value, SHparameter = 10
        };
        // XXX: only use one light here. 
        UseImage.Effect.Custom = material.ApplyLight(light).Apply(Normals, UseImage.Size, eyePosition);

        //if (false) {
        //  center = new PointBl(0.5, 0.5);
        //  image.Effect.Custom = (Input, uv) => {
        //    var tangent = new Point3DBl((uv - 0.7) * image.Size, 0).Cross(Normals[uv]).Normalize; 
        //    return light.Diffraction(eyePosition, new Point3DBl(uv * image.Size, 0), Normals[uv], tangent);
        //  };
        //}
      }
    }.RightTop;

    At = new ButtonBl(canvas) {
      LeftTop = At, Content = "Use caculated normal",//calculate the normal as you want
      Click = () => {
        slider.Visibility = false;
        UseImage.Source = new BitmapImage("Resources/Autumn.jpg".MakePackUri(typeof(Shaders)));
        var wall = new Tex2D(UV => {
          var t = (UV.X + (UV.Y % 0.2 > 0.1).Condition(0.1, 0)) % 0.2;
          var t2 = UV.Y % 0.1;
          BoolBl stride = (t).Abs < 0.015 | (t2).Abs < 0.016;
          return stride.Condition(Colors.Tan.Bl(), Colors.Firebrick);
        });
        UseImage.Effect.Custom = (Input, uv) => {
          var t = (uv.X + (uv.Y % 0.2 > 0.1).Condition(0.1, 0)) % 0.2;
          var tx = (t - 0.1).Pow(2) * 100;//1
          tx = (t > 0.1).Condition(tx, -tx);
          var t2 = uv.Y % 0.1;
          var ty = (t2 - 0.05).Pow(2) * 1000;//0.25
          ty = (t2 > 0.05).Condition(ty, -ty);
          BoolBl stride = (t).Abs < 0.015 | (t2).Abs < 0.016;
          var noise = ShaderLib.Noise[uv].XXZ() * 2 - 1;
          return diffMaterial.ApplyLight(light).ApplyAt(new Point3DBl(uv * UseImage.Size, 0), stride.Condition(noise, new Point3DBl(tx, ty, 1).Normalize)).ForPixel(Input, uv);
          //var newNorm = ( new Point3DBl(stride.Condition(noise.ScG, tx), stride.Condition(noise.ScB, ty), 1).Normalize + 1 ) * 0.5;
          //return new ColorBl(new RGBBl(newNorm), 1.0);
        };
      }
    }.RightTop;
   }
  }
}
